package com.jmxcfc.blfsc.langyu.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jmxcfc.blfsc.common.sms.SmsConstants;
import com.jmxcfc.blfsc.common.common.request.BaseRequest;
import com.jmxcfc.blfsc.common.common.response.BaseResponse;
import com.jmxcfc.blfsc.common.exception.*;
import com.jmxcfc.blfsc.common.sms.BaseContext;
import com.jmxcfc.blfsc.common.sms.SmsRequest;
import com.jmxcfc.blfsc.common.common.enums.ProcessStateEnum;
import com.jmxcfc.blfsc.common.sms.extend.SmsExtend;
import com.jmxcfc.blfsc.common.validation.rule.RequestFieldRule;
import com.jmxcfc.blfsc.common.validation.rule.SmsRulesUtil;
import com.jmxcfc.blfsc.langyu.common.Constants;
import com.jmxcfc.blfsc.langyu.entity.SendDetailSub;
import com.jmxcfc.blfsc.langyu.enums.ResponseStatusEnum;
import com.jmxcfc.blfsc.langyu.service.ISendDetailSubService;
import com.jmxcfc.blfsc.langyu.service.ISmsRequestService;
import com.jmxcfc.blfsc.langyu.util.LangYuCommonUtils;
import com.jmxcfc.blfsc.langyu.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 * 短信记录表 服务实现类
 * </p>
 *
 * @author pbs
 * @since 2023-09-06 2:38:55
 */
@Slf4j
@Service
@RefreshScope
public class SmsRequestServiceImpl implements ISmsRequestService {

    @Value("${com.langyu.url}")
    private String url;

    @Value("${com.langyu.sign}")
    private String sign;


    private ISendDetailSubService sendDetailSubService;
    private RestTemplate restTemplate;



    /**
     * 发送短信
     * 校验:
     * requestNo(内部的请求流水号)不为空、
     * 批次号(批次号,对应接口朗宇参数transactionId字段)不为空、
     * sendDtl(发送集合)不为空
     * 扩展字段检验:
     * 账号、密码
     * @param requestMessage
     * @return
     */
    @Override
    public BaseResponse<JSONObject> sendRequest(BaseRequest<SmsRequest<SmsExtend>> requestMessage) {
        BaseContext<String> baseContext = new BaseContext<>();
        //头部参数校验规则:业务号、请求流水号字段(朗宇参数rid)
        List<RequestFieldRule<?>> headerElementRules = baseContext.getHeaderElementRules();
        headerElementRules.add(SmsRulesUtil.getRequestNoRule());
        headerElementRules.add(SmsRulesUtil.getSerialSeqRule("批次号字段(朗宇参数rid)规则检验不符合"));

        //请求体参数校验:发送集合
        List<RequestFieldRule<?>> bodyElementRules = baseContext.getBodyElementRules();
        bodyElementRules.add(SmsRulesUtil.getSendDtlRule());
        List<RequestFieldRule<?>> bodyExtendElementRules = baseContext.getBodyExtendElementRules();
        //请求体扩展字段校验:账号、密码
        bodyExtendElementRules.add(SmsRulesUtil.getAcctNolRule("账号字段规则检验不符合"));
        bodyExtendElementRules.add(SmsRulesUtil.getAcctPasswordRule("密码字段规则检验不符合"));
        return execute(requestMessage, baseContext);
    }

    /**
     * 重写扩展字段的校验:校验账号、密码字段
     * @param body (不能传JSONObject)
     *备注:每个实现类需要自己实现该方法,然后进行扩展字段的校验
     * @param baseHandleContext
     * @param <E>
     * @throws ValidBodyException
     */
    @Override
    public <E> void validBodyExtend(SmsRequest<SmsExtend> body, BaseContext<E> baseHandleContext) throws ValidBodyException {
        try {
            if (Objects.nonNull(baseHandleContext)) {
                baseHandleContext.getBodyExtendElementRules().forEach(rule -> valid(rule, body.getExtend()));
            }
        } catch (RuntimeException e) {
            throw new ValidBodyException(e.getMessage());
        }
    }

    /**
     * 请求参数组装
     * 1、serialSeq=朗宇的transactionId事务ID 字段
     * 2、扩展字段:acctNo账号、acctPassword密码
     * @param request
     * @param response
     * @param baseContext
     * @throws BeforeOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void beforeOutbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws BeforeOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        String serialSeq = request.getHeader().getSerialSeq();
        try {
           
            log.info("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},朗宇前置处理:数据落库和参数组装",requestNo,serialSeq);
            List<SendDetailSub> sendDetailSubList = new ArrayList<>();
            buildEntity(request, sendDetailSubList);
            //保存数据
            saveBatch(requestNo, sendDetailSubList);
            baseContext.setExtendStr(JSON.toJSONString(sendDetailSubList));

            log.info("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},保存朗宇明细表数据条数:{},前置处理结束", requestNo, serialSeq, sendDetailSubList.size());
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},前置处理异常:{}", requestNo, e.getMessage(), e);
            throw new BeforeOutboundException(e.getMessage(), e);
        }
    }

    /**
     * 外呼请求
     * @param request
     * @param response
     * @param baseContext
     * @throws OutboundException
     */
    @Override
    public void outbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws OutboundException {
        try{

            //发送成功数量
            AtomicInteger sucNum = new AtomicInteger();
            //发送失败数量
            AtomicInteger failNum = new AtomicInteger();
            //发送成功list
            List<JSONObject> sucList = new ArrayList<>();
            //发送失败list
            List<JSONObject> failList = new ArrayList<>();
            List<SendDetailSub> sendDetailSubList = JSONUtil.toList(baseContext.getExtendStr(), SendDetailSub.class);
            sendDetailSubList.forEach(sendDetailSub ->{
                Map<String,String> requestParamStr = buildParam(request, sendDetailSub);
                HttpEntity<MultiValueMap<String, String>> stringHttpEntity = buildHttpEntity(requestParamStr);

                log.info("短信请求业务号requestNo:{},朗宇发送短信请求外呼,请求地址:{},请参数:{}",request.getHeader().getRequestNo(),url,stringHttpEntity);
                String resp = restTemplate.postForObject(url, stringHttpEntity, String.class);
                log.info("短信请求业务号requestNo:{},朗宇发送短信请求外呼,响应内容:{}", request.getHeader().getRequestNo(), resp);
                JSONObject restResponse = JSON.parseObject(resp);
                if(Constants.RESP_FLAG_SUCCESS.equals(restResponse.getString(Constants.RESP_RESULT))){
                    sucNum.getAndIncrement();
                    JSONObject success = new JSONObject();
                    success.put(Constants.UUID,sendDetailSub.getUuid());
                    success.put(Constants.RESP_RESULT,restResponse.getString(Constants.RESP_RESULT));
                    success.put(Constants.RESP_MSG_ID,restResponse.getString(Constants.RESP_MSG_ID));
                    sucList.add(success);
                }else{
                    failNum.getAndIncrement();
                    JSONObject fail = new JSONObject();
                    fail.put(Constants.UUID,sendDetailSub.getUuid());
                    fail.put(Constants.RESP_RESULT,restResponse.getString(Constants.RESP_RESULT));
                    fail.put(Constants.RESP_MSG_ID,restResponse.getString(Constants.RESP_MSG_ID));
                    failList.add(fail);
                }
            });

            //全部请求结果
            JSONObject completeResponse = new JSONObject();
            completeResponse.put(Constants.RESP_SUCCESS,sucNum.get());
            completeResponse.put(Constants.RESP_SUC_LIST,sucList);
            completeResponse.put(Constants.RESP_FAIL,failNum.get());
            completeResponse.put(Constants.RESP_FAIL_LIST,failList);
            baseContext.setRestResponse(completeResponse.toJSONString());
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},朗宇发送短信请求外呼异常:{}", request.getHeader().getRequestNo(), e.getMessage());
            throw new OutboundException(e.getMessage(), e);
        }
    }
    /**
     * 后置处理:
     * 根据朗宇响应的result字段进行判断发送状态:
     * 1、result=0:下行成功,终态需要等待回调通知,根据requestNo、transactionId,更新朗宇明细表数据
     发送状态=>处理中(SmsSendStatusEnum.PROCESSING)
     * 2、result<>0:表示发送失败,将失败数据添加至列表failList
     注意:此时需要把失败列表响应给sms
     发送状态=>失败(SmsSendStatusEnum.FAILURE)
     * 超出范围1、2的判断则置为异常,不更新数据
     * success=true或者false都是 接口交互状态都是:response.setApiStatusEnum(ApiStatusEnum.SUCCESS);
     *
     *4、更新数据规则:
     (1)全部成功时,根据requestNo、batchId 条件 更新本批次全量数据
     (2)存在部分失败时:先根据requestNo、batchId 条件 更新本批次全量数据,后再更新失败的(具体失败的原因)
     表中字段 success 如果时false的值,且 errorCode为空值,则说明本号码是交互成功,但是存在部分失败的号码
     * @param request
     * @param response
     * @param baseContext
     * @throws AfterOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void afterOutbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws AfterOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try{
            log.info("短信请求业务号requestNo:{},后置处理:解析响应结果", requestNo);
            JSONObject restResponse = JSON.parseObject(baseContext.getRestResponse());
            //failList有值 表示部分或全部失败
            int sucNum = Optional.of(restResponse.getIntValue(Constants.RESP_SUCCESS)).orElse(0);
            int failNum = Optional.of(restResponse.getIntValue(Constants.RESP_FAIL)).orElse(0);
            String success = "true";
            String successDesc = "下行成功";
            log.info("短信请求业务号requestNo:{},朗宇响应的成功数量为:{},失败数量为:{}", requestNo, sucNum,failNum);
            //有对接对方异步通知接口
            response.getHeader().setProcessState(ProcessStateEnum.NOTIFICATION);
            JSONObject responseBody = new JSONObject();

            //成功记录
            List<SendDetailSub> sucSendDetailSubList = new ArrayList<>();
            //失败记录
            List<SendDetailSub> failSendDetailSubList = new ArrayList<>();
            if(sucNum > 0){
                //成功部分
                List<JSONObject> sucList = (List<JSONObject>) Optional.ofNullable(restResponse.get(Constants.RESP_SUC_LIST))
                        .orElseGet(() -> {
                            log.error("短信请求业务号requestNo:{},朗宇响应成功数量为:{},但是sucList成功列表为空",requestNo,sucNum);
                            return Collections.emptyList();
                        });
                sucList.forEach(sucItem -> {
                    SendDetailSub sendDetailSub = new SendDetailSub();
                    sendDetailSub.setUuid(sucItem.getString(Constants.UUID));
                    sendDetailSub.setMsgId(sucItem.getString(Constants.RESP_MSG_ID));
                    sendDetailSub.setErrorCode(sucItem.getString(Constants.RESP_RESULT));
                    sendDetailSub.setErrorDesc(Objects.requireNonNull(ResponseStatusEnum.getEnumByValue(sucItem.getString(Constants.RESP_RESULT))).desc());
                    sucSendDetailSubList.add(sendDetailSub);
                });
            }
            if(failNum > 0){
                //失败部分
                List<JSONObject> failList = (List<JSONObject>) Optional.ofNullable(restResponse.get(Constants.RESP_FAIL_LIST))
                        .orElseGet(() -> {
                            log.error("短信请求业务号requestNo:{},朗宇响应失败数量为:{},但是failList失败列表为空",requestNo,failNum);
                            return Collections.emptyList();
                        });
                failList.forEach(failItem -> {
                    SendDetailSub sendDetailSub = new SendDetailSub();
                    sendDetailSub.setUuid(failItem.getString(Constants.UUID));
                    sendDetailSub.setMsgId(failItem.getString(Constants.RESP_MSG_ID));
                    sendDetailSub.setErrorCode(failItem.getString(Constants.RESP_RESULT));
                    sendDetailSub.setErrorDesc(Objects.requireNonNull(ResponseStatusEnum.getEnumByValue(failItem.getString(Constants.RESP_RESULT))).desc());
                    failSendDetailSubList.add(sendDetailSub);
                });
                responseBody.put(SmsConstants.RESP_FAIL_LIST, failList);
                response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
                successDesc = "存在全部或者部分失败";
                success = "false";
            }


            //如果存在成功记录,则更新成功记录
            if (CollectionUtils.isNotEmpty(sucSendDetailSubList)) {
                sendDetailSubService.updateBatchById(sucSendDetailSubList);
                log.info("短信请求业务号requestNo:{},朗宇响应成功部分,更新明细表(成功号码列表)条数:{}", requestNo, sucSendDetailSubList.size());
            }
            //如果存在失败记录,则更新失败记录
            if (CollectionUtils.isNotEmpty(failSendDetailSubList)) {
                sendDetailSubService.updateBatchById(failSendDetailSubList);
                log.info("短信请求业务号requestNo:{},朗宇响应失败部分,更新明细表(失败号码列表)条数:{}", requestNo, failSendDetailSubList.size());
            }
            //如果有失败列表必须返回,在sms模块需要进行处理
            response.getHeader().setResCode(success);
            response.getHeader().setResMessage(successDesc);
            //body里面存 failList
            response.setBody(responseBody);

        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},后置处理异常:{}", requestNo, e.getMessage(), e);
            throw new AfterOutboundException(e.getMessage(), e);
        }
    }


    /**
     * 数据保存
     * @param requestNo
     * @param sendDetailSubList
     */
    private void saveBatch(String requestNo,List<SendDetailSub> sendDetailSubList) {
        try {
            sendDetailSubService.saveBatch(sendDetailSubList);
        } catch (DuplicateKeyException e) {
            log.error("请求流水号requestNo:{},保存朗宇明细数据,存在重复主键Id,违反唯一约束条件", requestNo);
            throw new RepetitionException(e);
        }
    }



    /**
     * 实体类组装
     * @param request
     * @param sendDetailSubList
     */
    private void buildEntity(BaseRequest<SmsRequest<SmsExtend>> request, List<SendDetailSub> sendDetailSubList) {
        String requestNo = request.getHeader().getRequestNo();
        String serialSeq = request.getHeader().getSerialSeq();
        SmsRequest<SmsExtend> requestBody = request.getBody();
        
        if (CollectionUtils.isEmpty(requestBody.getSendDtl())){
            log.error("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},请求参数的发送集合是空", requestNo, serialSeq);
            throw new BeforeOutboundException("短信发送集合sendDtl字段为空!");
        }

        requestBody.getSendDtl().forEach(messageParamItem -> {
            //实体类
            SendDetailSub sendDetailSub = new SendDetailSub();
            sendDetailSub.setUuid(messageParamItem.getUuid());
            sendDetailSub.setRequestNo(requestNo);
            sendDetailSub.setTransactionId(serialSeq);
            sendDetailSub.setAccount(requestBody.getExtend().getAcctNo());
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            String ts = df.format(new Date(System.currentTimeMillis()));
            sendDetailSub.setTs(ts);
            sendDetailSub.setMobile(messageParamItem.getCellPhoneNo());
            sendDetailSub.setContent(messageParamItem.getContent());
            sendDetailSub.setNeedStatus(Constants.NEED_STATUS_TRUE);
            sendDetailSub.setRespType(Constants.RESP_TYPE_JSON);

            sendDetailSubList.add(sendDetailSub);
        });

    }

    /**
     * 参数组装
     * @param request
     * @param sendDetailSub
     */
    private Map<String,String> buildParam(BaseRequest<SmsRequest<SmsExtend>> request, SendDetailSub sendDetailSub) {
        String requestNo = request.getHeader().getRequestNo();
        String serialSeq = request.getHeader().getSerialSeq();
        SmsRequest<SmsExtend> requestBody = request.getBody();

        if (CollectionUtils.isEmpty(requestBody.getSendDtl())){
            log.error("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},请求参数的发送集合是空", requestNo, serialSeq);
            throw new BeforeOutboundException("短信发送集合sendDtl字段为空!");
        }
        Map<String,String> requestParam = new HashMap<>();
        //账号
        requestParam.put(Constants.ACCOUNT, requestBody.getExtend().getAcctNo());
        requestParam.put(Constants.TIMES, sendDetailSub.getTs());
        //密码=account+pwd+ts 按照顺序排列（不包含“+”字符）后,再经过标准 MD5加密(其中pwd 为客户密码,服务方提供)
        requestParam.put(Constants.PASSWORD, MD5Util.md5(StringUtils.join(requestBody.getExtend().getAcctNo(), requestBody.getExtend().getAcctPassword(), sendDetailSub.getTs())));
        requestParam.put(Constants.MOBILE,sendDetailSub.getMobile());
        requestParam.put(Constants.MSG,StringUtils.join(sign,sendDetailSub.getContent()));
        requestParam.put(Constants.NEED_STATUS,sendDetailSub.getNeedStatus());
        requestParam.put(Constants.UUID,sendDetailSub.getUuid());
        requestParam.put(Constants.RESP_TYPE,sendDetailSub.getRespType());
        log.info("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},参数组织完成,参数:{}",requestNo, serialSeq, JSON.toJSONString(requestParam));
        return requestParam;
    }

    private HttpEntity<String> creatRequestEntity(String params) {
        HttpHeaders headers = new HttpHeaders();
        //原先代码有
        /*Map<String, String> mapHeader = new HashMap<>();
        mapHeader.put("Connection","close");
        headers.setAll(mapHeader);*/
        //MediaType是 application/json;charset=UTF-8
        headers.setContentType(MediaType.parseMediaType(Constants.MEDIA_TYPE_APPLICATION_FORM_UTF8));
        return new HttpEntity<>(params, headers);
    }

    private HttpEntity<MultiValueMap<String, String>> buildHttpEntity(Map<String, String> dataMap) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> params = LangYuCommonUtils.mapToMultiValueMap(dataMap);
        return new HttpEntity<>(params, headers);
    }

    @Autowired
    public void setSendDetailSubService(ISendDetailSubService sendDetailSubService) {
        this.sendDetailSubService = sendDetailSubService;
    }

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }


}
